iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 11
2

寫在前面

遠在現在各種程式語言百花齊放,遠在java統治中土之前,
有一個語言是所有工程師們競相爭逐的目標,
有一個語言創造了整個歷史,
之於魔戒的重要程度刊比沒有了他連書名都命名不了
他就是
C
重要到我必須為了他空出一行

C語言作者的C程式設計語言在程式語言界也是相當重要的書
因為封面白色而被稱為白皮書(等級如同政界邱吉爾的白皮書)
而裡頭教學時使用印出Hello World來當作教學第一課也被後世引用至今
這樣你知道C語言的重要性了吧

後來為了擴充物件導向延伸出了C++
原本C跟C++是打算分成兩個語言來介紹的,畢竟在TIOBE這兩種語言也是分開的
但是C寫起來真的太反人類了,於是把他給併到C++來介紹

C++

他是為了擴充C語言而特別獨立出來的語言,新增了許多現今語言的特性
讓C更為接近人類而非機器
於是乎他可以像C語言一樣寫相當底層控制硬體的程式,也可以寫貼近人類的瀏覽器
舉幾個例子,像是微軟的Windows就是由C++寫的,還有蘋果的ios也是
常見的瀏覽器例如Chrome,Firefox都是用C++寫的
還有LOL也是(還記得當初安裝時他叫你先去安裝Microsoft Visual C++嗎?)

但是我還是不建議你從這個語言入手

除非你身邊有一個可以帶著你的老手
因為這個語言真的太複雜了

如果你已經有了心裡準備,先去安裝C++吧
linux跟MacOS安裝相對簡單
Windows相關的介紹這裡有

欸? 為什麼今天會有安裝的教學?

因為C++就連安裝都沒有很簡單

以下

讓我們進入C++的世界吧

在你的工作目錄下建立一個附檔名為.cpp的檔案
然後貼上下面的程式碼

#include <iostream>
#include <string>
#include <cmath>
using namespace std;

void t2h(string input);
int h2t(string input);
char returnAE(int input);
int AEreturn(char input);
int string2int(string input);

int main(void){
	string input;
	string number;

	cout << "Tell me what you want to do:\n";
	cout << "(1)T to H (2)H to T\n";
	cin >> input;
	if (input.length()>1){
		cout << "Wrong input\n";
	}else{
		switch (input[0]) {
			case '1':
				cout << "Please input the number:";
				cin >> number;
				t2h(number);
				break;
			case '2':
				cout << "Please input the number:";
				cin >> number;
				cout << h2t(number) << endl;
				break;
			default:
				cout << "Wrong input"<<endl;
		}
	}	
	return 0; 
}

int h2t(string input){
	int output = 0;
	int i = 0;
	while (i < input.length()){
		output = output + AEreturn(input[i])*pow(16,input.length()-i-1);
		i++;
	}
	return output;
}

int AEreturn(char input){
	switch (input){
		case  'A':
			return 10;
		case  'B':
			return 11;
		case  'C':
			return 12;
		case  'D':
			return 13;
		case  'E':
			return 14;
		case  'F':
			return 15;
		default:
			return input-'0';
	}
}

void t2h(string input){
	int number = string2int(input);	
	for (int i = 0;i<16;i++){
		for (int j=0;j<16;j++){
			for (int k=0;k<16;k++){
				if ( pow(16.0,2.0)*i+pow(16.0,1.0)*j+pow(16.0,0.0)*k == number){
					cout << returnAE(i) << returnAE(j) <<returnAE(k) <<endl;
			 	}
			}
		}
	}		
}

char returnAE(int input){
	switch (input){
		case 10:
			return 'A';
		case 11:
			return 'B';
		case 12:
			return 'C';
		case 13:
			return 'D';
		case 14:
			return 'E';
		case 15:
			return 'F';
		default :
			return input + '0';
	}
}

int string2int(string input){ //字串轉型工具
	int output = 0;
	for (int i= 0;i<input.length();i++){
		int temp = input[i]-'0';
		output = output + temp*pow(10.0,input.length()-i-1);	
	}
	return output;
}

是不是比以往看過的程式都要來得複雜?

現在逃還來得及,逃避雖然可恥但是有用

先玩一下c++的編譯吧
linux系統請使用

g++ test.cpp 

windows系統請使用

gcc -o test test.cpp

這個指令可以讓你編譯test.cpp這個檔案
之後你應該會發現同一個資料夾下多了一個檔案a.out(linux)或是a.exe(windows)
你可以直接執行這個檔案

沒錯,c++這個語言會直接編譯出執行檔案,而不像java及C#需要在特定環境執行

接下來我們一步一步解釋c++在做什麼吧

import

#include <iostream>
#include <string>
#include <cmath>
using namespace std;

雖然我把include跟using一起放在import介紹,但是其實兩者是相當不同的喔

#include是指匯入某些函式庫(想成一堆幫你寫好的方法的集合體)
而using則是類似C#的using,引入這個命名空間,
其目的在於讓你省下每次使用命名空間裡面的方法時都需要打std的時間

所以從根本上來說#include跟using是兩回事
當你使用被命名空間包裝過的函式庫時你就必須使用命名空間來使用函式庫
比方說std::cin,
當今天你不想每次使用cin都在前面加命名空間時你可以在程式開頭using,節省時間

有時候你會看到這種用法

#include <math.h>

雖然這是C的寫法,但是在C++也是可以通的
只是為了區分兩種語言的math函式庫,C語言使用.h結尾,而C++會在前面加上c
詳細可以到這裡看看

方法

void t2h(string input);
int h2t(string input);
char returnAE(int input);
int AEreturn(char input);
int string2int(string input);

注意到這堆東西,C++一個相當反人類的地方在於方法在正式寫裡頭在做什麼之前需要先宣告他的輸入及輸出
所以當你還在修改你的函式時很長需要同時改兩個位置的程式碼
(main方法除外)

我們來看看main方法

int main(void){
	string input;
	string number;

	cout << "Tell me what you want to do:\n";
	cout << "(1)T to H (2)H to T\n";
	cin >> input;
	if (input.length()>1){
		cout << "Wrong input\n";
	}else{
		switch (input[0]) {
			case '1':
				cout << "Please input the number:";
				cin >> number;
				t2h(number);
				break;
			case '2':
				cout << "Please input the number:";
				cin >> number;
				cout << h2t(number) << endl;
				break;
			default:
				cout << "Wrong input"<<endl;
		}
	}	
	return 0; 
}

注意到main方法是有回傳值的喔
這是為了確認程式是否有被正確執行
如果有則回傳0,沒有則可以回傳其他數字,工程師們便可以利用回傳的數字判斷程式出了什麼問題
(類似網頁的404)
有興趣的可以往這方面去查詢(FreeBSD的例子)

看到我們宣告了字串了嗎?

	string input;
	string number;

大家應該要謝天謝地
因為我們引入了函式庫 string
這是C++有而C沒有的(也是我為什麼不寫C而直接寫C++的原因)

在C你只能這樣寫

	char input[5];
	char number[5];

還記得我們之前說字串其實是一堆的字元組成的嗎?
在C這種比較早期的語言中,甚至根本就沒有字串
只能使用字元組成的陣列,因此你必須在一開始就決定好字串的長度
而且你之後輸入時長度其實可以超過當初定義好的(這牽涉到C儲存字元的方式)
所以如果沒有搞清楚C實際對記憶體做了什麼的話很容易出BUG
看不懂?沒關係,去問那個當初叫你先學C++的朋友

輸入及輸出

	cout << "Tell me what you want to do:\n";
	cout << "(1)T to H (2)H to T\n";
	cin >> input;

這個就是C++的輸入及輸出,跟其他語言相當不同吧
語法相當統一,一個in一個out

如果我們當初沒有引入std的namesapce的話需要這麼寫

	std::cout << "Tell me what you want to do:\n";
	std::cout << "(1)T to H (2)H to T\n";
	std::cin >> input;

如果最後面你不想加換行符號你也可以使用endl,例如

std::cout << "(1)T to H (2)H to T" << endl;

(注意是endl不是end1,應該是klmn的l,而不是123的1)
這樣他就換幫你換行

得到輸入值後我們必須先判斷長度是否為1

if (input.length()>1){
	cout << "Wrong input\n";
}

length()在C++當中是string這個物件的方法,可以告訴你這個字串的長度
先判斷長度是否為1,超過的話鐵定錯誤,因此回給使用者說他輸入錯誤

什麼是物件的方法?

這個牽涉到物件導向的概念,我們之後會專門講解(老高上身)
這邊你可以當作這個方法可以幫你算出這個字元的陣列有多長

switch

switch (input[0]) {
	case '1':
		cout << "Please input the number:";
		cin >> number;
		t2h(number);
		break;
	case '2':
		cout << "Please input the number:";
		cin >> number;
		cout << h2t(number) << endl;
		break;
	default:
		cout << "Wrong input"<<endl;
}

好,由於我們剛才宣告的input是一個陣列,因此就算我們只輸入一個值也需要使用input[0]將第一個字元取出

接著注意到這裡

case '1': 

如同我前面說的,C++是強型別語言,只有相同型別的語言才能比較
而由於input是字元陣列,因此取出的值一定是字元
因此如果要比較的話case的值就一定要是字元
在C/C++分得很清楚,字元就是使用兩個'包起來,而字串則是兩個"
有些語言沒有區分,但是在這裡如果你使用兩個"的話會報錯,因為型別不同

不能像其他語言一樣直接switch字串嗎?

C++的switch只能判斷整數型別(int)跟枚舉型別(一種有趣的型別,不過我們應該不會介紹到)

只能判斷整數型別為什麼可以輸入字元?

原因我們之後會說明,先往下看吧

方法的結構

先看到AEreturn

int AEreturn(char input){
	switch (input){
		case  'A':
			return 10;
		case  'B':
			return 11;
		case  'C':
			return 12;
		case  'D':
			return 13;
		case  'E':
			return 14;
		case  'F':
			return 15;
		default:
			return input-'0';
	}
}

這段其實沒有寫得很好,因為如果使用者輸入G之類的就會出錯,大家可以想看看解決方案

C++的方法結構如下

回傳型別 方法名稱(參數型別 參數名稱){}

因為是靜態型別,因此不管是輸入值還是回傳值都需要先定義好型別
這裡回傳值的型別為int,輸入值的型別為字元char

字元與整數

注意到default的回傳值

input-'0';

這啥? 為什麼字元可以-?

理由在於C++在int跟char之間是可以隱式轉換的
你可以嘗試執行下面的程式碼

#include <iostream>

int main(){
	char a = 9 + '0'; //將整數型別與字元型別相加後丟到字元型別內
	std::cout << a << std::endl; //使用原本的型別印出
	std::cout << (int)a << std::endl; //使用整數型別印出
	int b = 9 + '0';
	std::cout << b << std::endl;
	std::cout << (char)b << std::endl;
}

應該會得到

9
57
57
9

而這個隱式轉換的機制來自ASCII表

char '0' '1' '2' '3' '4' '5' '6' '7' '8' '9'
十進位 48 49 50 51 52 53 54 55 56 57

這也是為什麼字元的9使用整數來表達時會是57

因此如果要將整數而轉型回去的方法就是 +'0'
也就是將原本的數字0加上字元'0'的數值48後變成數值的48,也就是字元的0

可以看看returnAE的這段

default :
	return input + '0';

這樣你知道為什麼switch明明只能填入整數我們卻可以丟入字元了吧?
理由在於整數跟字元c++都會自動幫我們進行轉換

這樣我們學會了一個字元的轉型方式,就可以寫看看如果需要一整段的字元陣列該怎麼做了
往下看到這段程式碼

int string2int(string input){ //字串轉型工具
	int output = 0;
	for (int i= 0;i<input.length();i++){
		int temp = input[i]-'0';
		output = output + temp*pow(10.0,input.length()-i-1);	
	}
	return output;
}

這裡我們將整個字元陣列(字串)取出,並且根據字串的長度與他們對應的位置計算出數值後加總
(長度為三就將第一個位置的數字乘上100)
作法有點類似十六進位在做計算那樣

其實就只是將字元或數值的ASCII碼做運算而已,因此實際上並不太能稱作轉型,但是以我們目前使用來說已經夠了

double轉型

接著我們回到h2t這個方法

output = output + AEreturn(input[i])*pow(16,input.length()-i-1);

這段是某些人認為C++是弱型別的原因
pow是cmath這個函式庫提供的方法,用於計算冪次
計算出來的回傳值的型別應該是double(一種高精度的數值型別),可以跟AEreturn的int型別相乘並丟入int型別中

某一派的人認為這個是C++微弱型別的特徵,而另外一派的人認為數值之間的轉型並不能就此定義他是弱型別
但是就大部分的情況而言我們都可以把C++當作是強型別的語言

再補充一下cout

注意一下t2h的這一行

cout << returnAE(i) << returnAE(j) <<returnAE(k) <<endl;

其實就是把三個字元接在一起,最後加上一個換行符號,其實還有其他炫技的方式,不過今天就先這樣就好

以上我們把C++最膚淺的語法介紹完了(拿掉該死的指標跟字元在記憶體的儲存方式)

我們來複習一下C++的基本語法吧

  • 基本結構
    • 注意include跟using是完全不同的東西
    • 函式必須提前宣告結構
  • 印出/讀取 一個是cin一個是cout
  • 方法的結構
    • 靜態型別,方法必須提宣告回傳值的型別
    • 沒有方法關鍵字,直接是回傳型別開頭
  • 邏輯控制
    • switch (跟C#類似)
    • if (跟C#一樣)
  • 迴圈控制
    • for (跟C#一樣)
    • while (跟C#一樣)
  • 型別
    • 是否是弱型別要看對弱型別的定義
    • ASCII碼的計算方式與查表
  • 冪次計算 (跟C#類似)

到目前為止我們已經把最有名的幾個語言學完了

希望你有對其中幾種語言產生興趣
這時候該來學一些基本概念了
比方說物件


上一篇
typescript 有時候限制反而會是一件好事
下一篇
物件 萬物皆物件
系列文
你會十五種程式語言?不,我會十五種HelloWorld.為了避免這種狀況,因此寫了這篇:淺入淺出十五種程式語言30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
skycover
iT邦新手 4 級 ‧ 2020-09-11 23:18:42

如果有任何寫不清楚或是觀念沒有很明白的話請留言告知我
會盡快補上

如果有任何寫錯的地方也麻煩留言告知我
會盡快修正

感謝各位

我要留言

立即登入留言